
/*
 * Enlace.cpp
 *
 *  Created on: 28/06/2011
 *      Author: Erick
 */

//includes
#include <stdlib.h>
#include <stdio.h>
#include <cstring>
#include "Enlace.hpp"
#include "Bits.h"
#include "Layer.hpp"
#include "ConfigLoader.hpp"
#include <boost/thread.hpp>
#include <iostream>
#include <vector>
#include <stdexcept>

using namespace std;
using namespace ufpb::project::network;
using namespace utils;


/**
 * Construtor: realiza a inicializa��o do enlace
 * @param:
 * @return: um objeto do Enlace
 */
Enlace::Enlace(int mac) : Layer(0) {

    Enlace::monitor = 13;
    // TODO: mac e a prioridade sera o das configuracoes
    ConfigLoader *conf = new ConfigLoader("cfg.txt");
    //Enlace::mac = atoi(conf->getValue("mac")->c_str());
    this->mac = mac;
    Enlace::prioridade = 0;

    Enlace::mensagensDescartadas = new bool[1 << TAMANHO_MAC];
    Enlace::mensagensRecebidas = new Bits*[1 << TAMANHO_MAC];
    memset(Enlace::mensagensRecebidas, 0, 1 << TAMANHO_MAC);

    for (int i = 0; i < 1 << TAMANHO_MAC; i++) {

        Enlace::mensagensRecebidas[i] = new Bits(0);
    }

    for (int i = 0; i < 1 << TAMANHO_MAC; i++) {

        Enlace::mensagensDescartadas[i] = false;
    }

    this->querEnviar = false;
    this->querLock = new boost::mutex();
    this->naoEnviaLock = new boost::mutex();
    this->PodeEnviar = true;

}

/**
 * Destrutor de Enlace
 * @param: nenhum
 */
Enlace::~Enlace() {
}

/*
 * Quando o enlace tem alguma mensagem para enviar para a camada f�sico
 * @param: Bits* dados, os bits que ser�o enviados para a camada f�sico *
 */
void Enlace::newMessageToSend(Bits* dados) {


	cout << "Camada de Enlace mac "<< mac <<" -Novos dados vindo da camada de Redes -> ";
	dados->println();

	/**
	 * Vetor de bits que divide os quadros que ser�o enviados
	 */
    vector<Bits*> listQuadros = Enlace::EnquadraBits(dados);

    cout << " Camada de Enlace mac "<< mac <<" - quantidade de pacotes a serem enviados ";
    cout << listQuadros.size() << endl;


    // se a lista de quadros for vazia, retorna
    if (listQuadros.size() == 0)
        return;
    //trava o envia
    boost::unique_lock<boost::mutex> lock(this->envia);

    //varre a lista de quadros para enviar, envia cada parte dos dados que foram divididos
    for (std::vector<Bits*>::iterator it = listQuadros.begin(); it != listQuadros.end(); it++) {
        //diz que quer enviar alguma coisa
//    	cout << "antes";
    	this->setQuerEnviar(true);
//    	cout << "dentro";
    	//enquando nao puder enviar fica na espera
        while (!this->getPodeEnviar()) {

            this->condEnvia.wait(lock);
        }
        //quando puder enviar, envia para a camada f�sica
        cout << "Camada de Enlace - enviando ";
        (*it)->println();
        this->getDownLayer()->send(*it);
        //diz que nao quer mais enviar nada
        this->setQuerEnviar(false);
        //diz que nao enviar
        this->setPodeEnviar(false);
        //notificar que acabou o envio
        this->condRecebe.notify_all();
    }



}

/**
 * Quando eh necessario mudar o boolean de querer enviar
 * trava o boolean, e depois seta
 * @param: o boolean que quer enviar
 */
void Enlace::setQuerEnviar(bool querEnviar) {
    this->querLock->lock();
    this->querEnviar = querEnviar;
    this->querLock->unlock();

}

/**
 * Quando eh necessario mudar o boolean de poder enviar
 * trava o boolean, e depois seta
 * @param: o boolean que pode enviar
 */
void Enlace::setPodeEnviar(bool PodeEnviar) {
    this->naoEnviaLock->lock();
    this->PodeEnviar = PodeEnviar;
    this->naoEnviaLock->unlock();

}

/**
 * Quando se quer o boolean que quer enviar
 * @return: o boolean dizendo se alguem quer enviar
 */
bool Enlace::getQuerEnviar() {
    this->querLock->lock();
    bool retorno = this->querEnviar;
    this->querLock->unlock();
    return retorno;

}

/**
 * Quando se quer o boolean que pode enviar
 * @return: o boolean dizendo se alguem pode enviar
 */
bool Enlace::getPodeEnviar() {
    this->naoEnviaLock->lock();
    bool retorno = PodeEnviar;
    this->naoEnviaLock->unlock();
    return retorno;

}

/*
 *Quando o enlace tem alguma coisa para receber
 *@param: os bits quem chegam
 */
void Enlace::newReceivedMessage(Bits* dados) {
    cout << "Enlace recebeu: ";
    dados->println();
	if(dados->length() == 7) {
		cout << "tem tam 7, vou diminuir pra 4"<<endl;
        Bits *tmp = new Bits(4);
        tmp->copyBits(dados,0,4,0);
        delete dados;
        dados = tmp;
        cout << "dados diminuido ";
        dados->println();
    }
//	else {
//		cout <<" mais de 7 ";
//		dados->println();
//		sleep(5);
//	}
    cout << "Camada de Enlace mac "<< mac <<"- Token recebido no Enlace vindo do Fisico" << endl;
    cout << "tam " << dados->length();
      /***   VERIFICO O ESTADO DOS DADOS ****/
    /*
    1) CONTEM ERROS
    2) NÃO CONTEM ERROS E É DADOS
    3) NÃO CONTÉM ERROS E É TOKEN
     */
    // TOKEN CONTEM ERRO -- se contem erros cria novo token
    if (ChecaErros(dados)) {
        cout << "Camada de Enlace mac "<< mac <<"- Token incorreto detectado no Enlace: Criando novo token" << endl;
        // Checa se é erro de CRC
        // Se (é broadcast OU mac = meu endereco )E é dados E dados >= tamanho do quadro
        // calcula o crc8 e compara com o que tem no quadro
        if ((dados->get(LOCALIZACAO_BROADCAST) || mac == dados->takeInfo(LOCALIZACAO_DESTINO, TAMANHO_MAC))
                && dados->get(LOCALIZACAO_TIPO) && dados->length() >= TAMANHO_QUADRO) {
            int macOrigem = dados->takeInfo(LOCALIZACAO_ORIGEM, TAMANHO_MAC);
            int crcInformado = dados->takeInfo(dados->length() - TAMANHO_CRC, TAMANHO_CRC);
            int crcCalculado = CheckCrc8(dados->takeBits(TAMANHO_TOKEN, dados->length() - TAMANHO_CRC - TAMANHO_TOKEN));
            if (crcInformado != crcCalculado) {

                mensagensDescartadas[macOrigem] = true;

                mensagensRecebidas[macOrigem] = NULL;
            }
        }
        // Envia um novo token, pois o anterior contem erros
//        cout << "CRIANDO NOVO TOKEN INICIAL" << endl;
        sleep(1);
        delete dados;
        this->getDownLayer()->send(CriaToken(0));
    }
    else if (dados->get(LOCALIZACAO_TIPO)) { // SE NÃO CONTEM ERRO BITS e é dados
         cout << "Camada de Enlace mac "<< mac <<"- Token nao contem erros e e' DADOS" << endl;
        //Dados, se é broadcast OU mac = meu endereco
        if (dados->get(LOCALIZACAO_BROADCAST) || mac == dados->takeInfo(LOCALIZACAO_DESTINO, TAMANHO_MAC)) {

        	// Se é o destino, recebe o quadro
            // guarda a origem dos dados
            int macOrigem = dados->takeInfo(LOCALIZACAO_ORIGEM, TAMANHO_MAC);

            // se a mensagem deve ser descartada ignora a mensagem
            if (mensagensDescartadas[macOrigem]) {
                cout << "Camada de Enlace - msg vai ser descartada"<<endl;
                // Ignora o quadro
                if (!dados->get(LOCALIZACAO_INTERMEDIARIO))
                    mensagensDescartadas[macOrigem] = false;
            } else {// se a mensagem nao deve ser descartada leio o conteudo do quadro
                // Lê conteúdo do quadro
                cout << "Camada de Enlace - msg n vai ser descartada, lendo conteudo ";
                dados->println();

                Bits* novo = Desenquadra(dados);
                cout << "Camada de Enlace - msg desenquadrada ";
                novo->println();
                Bits* atual = mensagensRecebidas[macOrigem];
                cout << "Camada de Enlace - msg atual ";
                atual->println();



                if (atual->length() == 0) {
                    cout << "Camada de Enlace - primeira msg recebida do mac "<<macOrigem<<endl;
                    mensagensRecebidas[macOrigem] = novo;
                } else {
                    cout << "Camada de Enlace - adicionando msg recebida"<<endl;
                    // novo tamanho
                    Bits *merge = new Bits(novo->length() + atual->length());


                    // passa para o merge o conteudo atual de mensagensRecebidas[macOrigem]
//                    for (int i = 0; i < atual->length(); i++) {
//                        merge->set(i, atual->get(i));
//                    }
                    merge->copyBits(atual, 0, atual->length(), 0);
                    // copia para o merge o conteudo do atual
                    merge->copyBits(novo, 0, novo->length(), atual->length());

                    mensagensRecebidas[macOrigem] = merge;
                    mensagensRecebidas[macOrigem]->bit_count = merge->length();
                    cout << "Camada de Enlace - msg depois do merge ";
                    mensagensRecebidas[macOrigem]->println();
                }

                // Se e' o quadro final, passa para a camada superior
                if (!dados->get(LOCALIZACAO_INTERMEDIARIO) && (atual != NULL || novo != NULL)) {

                    cout << "Camada de Enlace - ultimo quadro, msg vai ser montada"<<endl;

                    Bits* cabecalho = new Bits(TAMANHO_MAC + 1);
                    cabecalho->set(0, dados->get(LOCALIZACAO_BROADCAST));
                    cabecalho->putInfo(1, TAMANHO_MAC, dados->takeInfo(LOCALIZACAO_DESTINO, TAMANHO_MAC));

                    // mensagem a ser enviada
                    Bits* myMessage = new Bits(cabecalho->length() + mensagensRecebidas[macOrigem]->length());
                    myMessage->copyBits(cabecalho, 0, cabecalho->length(), 0);

                    // passa para o merge o conteudo atual de mensagensRecebidas[macOrigem]
//                    for (int i = 0; i < cabecalho->length(); i++) {
//                        myMessage->set(i, cabecalho->get(i));
//                    }
                    
//                    cout << " mensagens recebidas"; mensagensRecebidas[macOrigem]->println();

//                                        dados->println();

//                                        system("pause");

                    // copia para o merge o conteudo do atual
                    myMessage->copyBits(mensagensRecebidas[macOrigem], 0, mensagensRecebidas[macOrigem]->length(), cabecalho->length());

                    cout << "Camada de Enlace mac "<< mac <<"- mensagem a ser enviada: ";
                    myMessage->println();
                      Layer *camada = this->getUpLayer();
                      camada->receive(myMessage);

                     // mensagensRecebidas[macOrigem] = new Bits(0);
                      delete mensagensRecebidas[macOrigem];
                      mensagensRecebidas[macOrigem] = new Bits(0);

//                     delete dados;
//                     delete novo;
//                     delete atual;
//                     delete cabecalho;
                }
            }

        }

        /********************** DECISÃO SOBRE O QUE FAZER COM O QUADRO*************************/
        // Se é a origem, remove o quadro
        if (mac == dados->takeInfo(LOCALIZACAO_ORIGEM, TAMANHO_MAC)) {
            this->RemoveQuadro(dados);
        } else if (mac == monitor) { // Se é monitor, checa o quadro
            this->MonitoraQuadros(dados);
        } else { // Se o quadro não interessa, repassa o quadro
            if (this->getQuerEnviar())
                this->ReservaToken(dados); // reserva o token

            Layer *camada = this->getDownLayer();
            camada->send(dados); // repassa o token
        }
    } else { // SE NÃO CONTÉM ERROS E É TOKEN
    	cout << "n tem erros e e' token"<<endl;
        if (this->getQuerEnviar() && this->ChecaPrioridade(dados)) {
            // Libera envio
        	cout << "quer enviar e tem prioridade"<<endl;
             boost::unique_lock<boost::mutex> lock(this->recebe);
            this->setPodeEnviar(true);
            condEnvia.notify_all();
            condRecebe.wait(lock);
        }
        else if (mac == monitor) { // Se É MONITOR, checa o quadro
        	cout << "eh monitor"<<endl;
             this->MonitoraQuadros(dados);
        } else { // Se o quadro não interessa, repassa o quadro
        	cout << "colocando token para 7"<<endl;
        	Bits* tmp = new Bits(7);
        	tmp->copyBits(dados,0,4,0);
        	delete dados;
        	dados = tmp;
        	cout << "enlace vai enviar o token ";
        	dados->println();
            this->getDownLayer()->send(dados);
        }
    }
}

/**
 * Funcao: init()
 * Função responsavel por criar o token inicial do anel
 *
 * **/

void Enlace::init() {

    Layer::init();

    // Se eu sou monitor eu Inicializo o anel
    if (mac == monitor) {
        Layer* camada = this->getDownLayer();
        camada->send(CriaToken(0));
    }

}

/**
 * Funcao: EnquadraBits
 * Função responsavel por enquandrar os bits recebidos
 * @param: Bits* dados (ponteiro para os dados)
 * @return: Um conjunto de Bits que representa os quadros
 *  **/

vector<Bits*> Enlace::EnquadraBits(Bits * dados) {




	int tamanho_mac = TAMANHO_MAC;
    // Nada a transmitir
    if (dados == NULL || dados->length() < tamanho_mac + 1) {
        vector<Bits*> empty; // empty list of ints
        return empty;
    }

    // Remove o endereço MAC do destino
    bool broadcast = dados->get(0);
    int macDestino = dados->takeInfo(1, tamanho_mac);

    dados = dados->takeBits(tamanho_mac + 1, dados->length() - tamanho_mac - 1);

//    cout << "quadro que saiu no Enquadra:";
//    	dados->println();
//    // Divide os dados em pacotes de tamanho igual
    vector<Bits*> conteudos;
    int tamanho_dados = TAMANHO_DADOS;
    int pacotes = dados->length() / tamanho_dados;


    for (int i = 0; i < pacotes; i++) {
        Bits *conteudo = new Bits(tamanho_dados);

        for (int j = 0; j < conteudo->length(); j++)
                 conteudo->set(j, dados->get(j*(i+1)));


        conteudos.push_back(conteudo);
    }




    // Se restar bits, coloca-os em um pacote menor
    if (pacotes * tamanho_dados < dados->length()) {
        Bits* conteudo = new Bits(tamanho_dados);
        for (int j = 0; j < conteudo->length(); j++)
            conteudo->set(j, dados->get((pacotes * tamanho_dados) + j));

        conteudos.push_back(conteudo);
    }

    // Enquadra os pacotes
    vector<Bits*> quadros;
    vector<Bits*> retorno_quadros;
    vector<Bits*> bits_enquadrados;

    int tamanho_quadros = TAMANHO_QUADRO;
    int localizacao_tipo = LOCALIZACAO_TIPO;
    int localizacao_monitor = LOCALIZACAO_MONITOR;
    int localizacao_prioridade = LOCALIZACAO_PRIORIDADE;
    int localizacao_broadcast = LOCALIZACAO_BROADCAST;
    int localizacao_intermediario = LOCALIZACAO_INTERMEDIARIO;
    int localizacao_dados = LOCALIZACAO_DADOS;
    int localizacao_destino = LOCALIZACAO_DESTINO;
    int localizacao_origem = LOCALIZACAO_ORIGEM;
    int tamanho = 0;
    int tamanho_token = TAMANHO_TOKEN;
    int tamanho_crc = TAMANHO_CRC;

    int tamanhoX = conteudos.size();



    for (int i = 0; i < tamanhoX ; i++) {

        tamanho = tamanho_quadros + conteudos.back()->length();
        // quadro
        Bits *quadro = new Bits(tamanho);

        // Colocando a prioridade para 0
        quadro->set(localizacao_prioridade, 0);
        quadro->set(localizacao_prioridade + 1, 0);
        // Resetando o bit do monitor
        quadro->reset(localizacao_monitor);
        // Setando o bit de tipo para DADOS
        quadro->set(localizacao_tipo);
        // Setando o bit de broadcast
        quadro->set(localizacao_broadcast, broadcast);
        // Setando o bit intermediario
        quadro->set(localizacao_intermediario);

        if(i == 0)
            quadro->set(localizacao_intermediario,0);

        // Colocando no quadro os enderecos MAC do destino e da origem
        quadro->putInfo(localizacao_destino, tamanho_mac, macDestino);
        quadro->putInfo(localizacao_origem, tamanho_mac, mac);

        // Colocando os dados
//        for (int j = 0; j < conteudos.back()->length(); j++)
//            quadro->set(localizacao_dados + i, conteudos.back()->get(0 + i));
        quadro->copyBits(conteudos.back(),0,conteudos.back()->length(),localizacao_dados);

        // Adiciona o quadro na lista de quadros
        quadros.push_back(quadro);

        // retira o ultimo
        conteudos.pop_back();

    }



    int tam = quadros.size();
    // Calcula os CRCs

//    quadros.back()->takeBits(tamanho_token, quadros.back()->length() - tamanho_crc - tamanho_token)->println();

    for (int i = 0; i < tam; i++) {
        // Calculo do crc
        int crc = CheckCrc8(quadros.back()->takeBits(tamanho_token, quadros.back()->length() - tamanho_crc - tamanho_token));

      //  cout << crc <<endl;
        // Setando o Crc no quadro

       quadros.back()->putInfo(quadros.back()->length() - tamanho_crc,tamanho_crc ,crc);

        retorno_quadros.push_back(quadros.back());
        quadros.pop_back();
    }

//    int tamanho_Vector = retorno_quadros.size();
//    // retorna com o ultimo sendo o primeiro da lista por causa do vector
//    for (int i = 0; i < tamanho_Vector; i++) {
//
//
//        bits_enquadrados.push_back(retorno_quadros.back());
//
//        retorno_quadros.pop_back();
//    }

    return retorno_quadros;
}

/**
 * Funcao: CriaToken
 * Cria um Token com a prioridade passada como parametro
 * @param: int prioridade
 * @return: Um conjunto de Bits que representa o Token com a prioridade
 **/

Bits * Enlace::CriaToken(int prioridade) {

    int tamanho_token = TAMANHO_TOKEN;
    int localizacao_tipo = LOCALIZACAO_TIPO;
    int localizacao_monitor = LOCALIZACAO_MONITOR;

    // Cria novo Token
    Bits* quadro = new Bits(7);
    quadro->reset(localizacao_tipo); // Tipo: permissao
    quadro->reset(localizacao_monitor); //Monitor: 0

    // Setando prioridade para a minha prioridade
    int posLP = LOCALIZACAO_PRIORIDADE;
    int posTP = TAMANHO_PRIORIDADE;

    quadro ->putInfo(posLP, posTP, prioridade);
    return quadro;

}

/**
 * Funcao: Desenquadra
 * Separa o quadro em token e dados e retorna apenas os dados
 * @param: Bits* quadro (ponteiro para um quadro de bits)
 * @return: Um conjunto de bits que contem os dados
 **/

Bits * Enlace::Desenquadra(Bits * quadro) {


    int localizacao_tipo = LOCALIZACAO_TIPO;
    int tamanho_quadro = TAMANHO_QUADRO;
    int localizacao_dados = LOCALIZACAO_DADOS;

    if (quadro == NULL || !quadro->get(localizacao_tipo) || quadro->bit_count < tamanho_quadro) {
        return NULL;
    } else {

        int tamanho = quadro->length() - tamanho_quadro;
        Bits *retorno = new Bits(tamanho);

//        retorno->copyBits(quadro,localizacao_dados,tamanho,0);
        for (int i = 0; i < tamanho; i++) {
            if (quadro->get(localizacao_dados + i)) {
                retorno->set(i);
            }
        }
        return retorno;
    }

}

/**
 * Método que monitora quadros
 * @param: Bits* dados (ponteiro para um conjunto de dados)
 */
void Enlace::MonitoraQuadros(Bits* quadro) {
	cout << "Monitora quadro ";
	quadro->println();
    // Se o quadro é igual a 0
    if (quadro->length() == 0) {
        return;
    }

    if (quadro->get(LOCALIZACAO_MONITOR)) {
         //cout << "monitor = 1" << endl;
        //descarta o quadro
        if (quadro->get(LOCALIZACAO_TIPO)) {
            cerr << "Enlace: Remove token marcado. Criando novo token." << endl;
        }
        int prioridadeAtual = quadro->get(LOCALIZACAO_TIPO) ? quadro->takeInfo(LOCALIZACAO_PRIORIDADE, TAMANHO_PRIORIDADE) : 0;


        if (this->getQuerEnviar() && this->prioridade >= prioridadeAtual) {

            boost::unique_lock<boost::mutex> lock(this->recebe);
            this->setPodeEnviar(true);
            condEnvia.notify_all();
            condRecebe.wait(lock);

        } else {
            //Envia um token
            sleep(1);
            this->getDownLayer()->send(this->CriaToken(prioridadeAtual));
        }
    } else {
        //Marca o quadro
        if (this->getQuerEnviar()) {
            this->ReservaToken(quadro);
        }
        quadro->set(LOCALIZACAO_MONITOR);
        this->getDownLayer()->send(quadro);
    }

}

/**
 * Método que remove os bits do quadro
 * @param: Bits* dados (ponteiro para um conjunto de dados)
 */
void Enlace::RemoveQuadro(Bits* quadro) {

    if (quadro->length() == 0)
        return;
    if (this->getQuerEnviar() && this->ChecaPrioridade(quadro)) {
        //libera o envio
        boost::unique_lock<boost::mutex> lock(this->recebe);
        this->setPodeEnviar(true);
        condEnvia.notify_all();
        condRecebe.wait(lock);
    } else {
        sleep(1);
        // Envia um token
        this->getDownLayer()->send(this->CriaToken(quadro->takeInfo(LOCALIZACAO_PRIORIDADE, TAMANHO_PRIORIDADE)));

    }

}

/**
 * Funcao: ChecaPrioridade
 * Funcao que checa se a prioridade do token é maior que a da camada
 * @param: Bits* quadro (ponteiro para um conjunto de dados ja enquadrados)
 * @return: false, se a prioridade é maior que a minha
 *          true, se a prioridade é menor que a minha
 **/
//

bool Enlace::ChecaPrioridade(Bits * quadro) {

    int localizacao_prioridade = LOCALIZACAO_PRIORIDADE;
    int tamanho_prioridade = TAMANHO_PRIORIDADE;
    int prioridadeQuadro = 0;


    // busca o valor da prioridade no quadro
    prioridadeQuadro = quadro->takeInfo(localizacao_prioridade, tamanho_prioridade);


    // Se o quadro for nulo ou se a prioridade do quadro é maior que a minha
    if (quadro == NULL || this->prioridade < prioridadeQuadro)
        return false;
    else
        return true;
}

/**
 * Funcao: ReservaToken
 * Funcao que reserva a prioridade do anel
 * @param: Bits* quadro (ponteiro para um conjunto de dados ja enquadrados)
 * @return: false, se a prioridade não é modificada
 *          true, se a prioridade é medoficada
 **/
bool Enlace::ReservaToken(Bits * quadro) {
    int posLP = LOCALIZACAO_PRIORIDADE;
    int posTP = TAMANHO_PRIORIDADE;

    //verifica o valor da prioridade
    int valorPrioridade = 0;

    for (int i = 0; i < posTP; i++) {
        valorPrioridade <<= 1;

        if (quadro->get(posLP + i))
            valorPrioridade++;
    }

    //se o quadro for null ou se a prioridade do enlace for menor que o valor da prioridade
    if (quadro == NULL || Enlace::prioridade < valorPrioridade) {
        return false;
    }
    //caso passe do if, se a priridade do enlace for 0, seta para 0
    if (Enlace::prioridade == 0) {
        quadro->set(posLP, 0);
        quadro->set(posLP + 1, 0);
        return true;
    }
    //se a priridade do enlace for 1, seta para 1
    else if (Enlace::prioridade == 1) {
        quadro->set(posLP, 0);
        quadro->set(posLP + 1, 1);
        return true;
    }
    //se a priridade do enlace for 2, seta para 2
    else if (Enlace::prioridade == 2) {
        quadro->set(posLP, 1);
        quadro->set(posLP + 1, 0);
        return true;
    }
    //se a priridade do enlace for 3, seta para 3
    else if (Enlace::prioridade == 3) {
        quadro->set(posLP, 1);
        quadro->set(posLP + 1, 1);
        return true;
    }
}

/* Funcao: ChecaErros()
 * Verifica se o quadro possui erros
 * @param: quadro de bits para realizar a checagem
 * return true, se contem erro e false se nao contem erros
 */
bool Enlace::ChecaErros(Bits * quadro) {

//    cout << "Camada de Enlace  mac " << mac << "quadro no checa erros "<<endl;
//    quadro->println();



	int tamanho_token = TAMANHO_TOKEN;
    int tamanho_crc = TAMANHO_CRC;
    int localizacao_tipo = LOCALIZACAO_TIPO;
    int tamanho_quadro = TAMANHO_QUADRO;
    int crc8;


    // Se o quadro é nulo ou o quadro é menor que deveria ser
    if (quadro == NULL || quadro->length() < tamanho_token) {
        return true;
    } else if (quadro->length() == tamanho_token) { // Se o quadro é dado e nao tem dado
        return quadro->get(localizacao_tipo);
    } else if (!quadro->get(localizacao_tipo) || quadro->length() < tamanho_quadro) {
        return true;
    }


    // tamanho do intervalo que sera avaliado
    int tamanhoIntervalo = quadro->length() - tamanho_token - tamanho_crc;


    // intervalo que sera avaliado
   // Bits* intervalo = new Bits(tamanhoIntervalo);

    // indice da localizacao do destino
    int indice = LOCALIZACAO_DESTINO-1;

    //Bits *intervalo = new Bits(tamanhoIntervalo);
    Bits *intervalo = quadro->takeBits(indice,tamanhoIntervalo);
    //intervalo->copyBits(quadro, indice, tamanhoIntervalo, 0);

     // calcula o crc8 do intervalo do quadro
    crc8 = CheckCrc8(intervalo);

    // Apos calcular o crc8 compara com o que esta no quadro

    // indice de inicio do intervalo
    int indiceQuadro = quadro->bit_count - (tamanho_crc);


    // armazenara o valordoCRC
    int valorCrc = 0;
    for (int i = 0; i < tamanho_crc; i++) {
        valorCrc <<= 1;
        if (quadro->get(indiceQuadro + i))
            valorCrc++;
    }

//    cout << "Camada de Enlace mac " << mac << ": crc8 " << crc8 << " valorCrc " << valorCrc << endl;;


    return crc8 != valorCrc;
}

/**
 * Funcao: CheckCrc8
 * Calcula o crc8 de um conjunto de dados
 * @param: Bits* dados (ponteiro para um conjunto de dados)
 * @return: o valor do calculo do crc8
 **/
int Enlace::CheckCrc8(Bits * dados) {
    int tamanhoQuadro = dados->bit_count;
    int bytes = tamanhoQuadro >> 3; // size() / 8
    int resto = (tamanhoQuadro & 0x07) == 0 ? 0 : 1; // size() % 8

    unsigned char* valor = new unsigned char[bytes + resto];
    //coloca todos os valores para 0 do array
    memset(valor, 0, bytes + resto);

    //coloca no array os valores byte a byte
    for (int i = 0, b = 0; b < bytes + resto && i < tamanhoQuadro; i += 8, b++) {
        int x = 8 * (b + 1) > tamanhoQuadro ? tamanhoQuadro - i : 8;
        for (int j = 0; j < x; ++j) {
            int bit = dados->get(i + j);
            //printf("bit: \n %d\n", bit);
            valor[b] <<= 1;
            valor[b] |= bit;

        }

    }

    int crc = 0;
    //faz a soma byte a byte do array
    for (int i = 0; i < bytes + resto; i++) {
        //	printf("CRC do FOR: %d\n", crc);
        //	crc = crc8[(crc ^ valor[i]) & 0xFF];
        crc += valor[i];
    }
    if(crc > 255)
    	crc = crc % 255;
    return crc;

}
